昨天學習 Node.js 的模組系統,今天要進入另一個開發中常使用的好朋友:Path 模組。
它的角色就像「地圖專家」,幫我們正確處理檔案路徑,
無論你在 Windows、macOS、Linux,寫出來的程式都能順利運作。
path?如果直接用字串拼路徑,常常會踩坑:
\,Linux/Mac 用 /。./、../ 稍不注意就會找錯地方。👉 這些問題交給 path,都能輕鬆解決。
project/
  ├── index.js
  ├── notes.txt
  ├── images/
  │     └── logo.png
  └── data/
        └── users.json
以下範例假設在 project/index.js 執行:
import path from "node:path";
const filePath = path.join("project", "data", "users.json");
console.log("完整路徑:", filePath);
console.log("檔名:", path.basename(filePath));
console.log("副檔名:", path.extname(filePath));
console.log("所在目錄:", path.dirname(filePath));
輸出:
完整路徑: project/data/users.json
檔名: users.json
副檔名: .json
所在目錄: project/data
path.join(...segments) → 安全拼接路徑(會自動處理 / 或 \)。path.resolve(...segments) → 一定會回傳絕對路徑。path.basename(p) → 取檔名(含副檔名)。path.extname(p) → 取副檔名。path.dirname(p) → 取目錄名稱。path.parse(p) → 把路徑拆成物件(root、dir、name、ext…)。path.format(obj) → 把物件重新組合成路徑。path.join() 與 path.**resolve**()的差異path.join()..、.)。path.join('project', 'data', 'users.json');
// → 'project/data/users.json'
path.join('/foo', '/bar', 'baz');
// → '/bar/baz'
path.resolve()path.resolve('project', 'data', 'users.json');
// → /Users/you/current-dir/project/data/users.json
path.resolve('/foo', 'bar', 'baz');
// → /foo/bar/baz
path.parse() 與 path.format()path.parse(p)把路徑拆解成物件:
const info = path.parse('/Users/you/project/data/users.json');
console.log(info);
結果:
{
  "root": "/",
  "dir": "/Users/you/project/data",
  "base": "users.json",
  "ext": ".json",
  "name": "users"
}
path.format(obj)把物件組回路徑:
const newPath = path.format({
  dir: '/Users/you/project/data',
  name: 'users.v2',
  ext: '.json'
});
console.log(newPath);
// → /Users/you/project/data/users.v2.json
C:\Users\you\project\data\users.json
/Users/you/project/data/users.json
./data/users.json
../images/logo.png
很多人會這樣寫:
import fs from "node:fs";
fs.readFile("./data/file.txt", "utf-8", (err, data) => {
  if (err) throw err;
  console.log(data);
});
這裡的 ./data/file.txt 不是程式檔案所在的資料夾,而是「程式執行時的工作目錄 (process.cwd())」。
./ vs __dirname vs process.cwd() 比較表| 名稱 | 代表什麼? | 特點 | 什麼時候用? | 
|---|---|---|---|
| ./ | 執行時的相對路徑 | 相對於「在哪個資料夾執行 node指令」 | 不穩定,容易出錯 | 
| __dirname | 程式檔案所在的資料夾 | 與程式碼位置綁定,不受執行位置影響 | ✅ 推薦用於讀寫檔案 | 
| process.cwd() | 程式執行時的工作目錄 | 與 ./等價,可用來檢查當前目錄 | CLI 工具、專案根目錄 | 
專案結構:
project/
  ├── index.js
  └── data/
       └── file.txt
情境一(在 project/ 內執行):
cd project
node index.js   # ✅ 可以找到 data/file.txt
情境二(在上一層執行):
cd ..
node project/index.js   # ❌ 找不到 data/file.txt
因為此時 ./ 指的是「上一層資料夾」,不是 project。
__dirnameimport fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const filePath = path.resolve(__dirname, "data", "file.txt");
fs.readFile(filePath, "utf-8", (err, data) => {
  if (err) throw err;
  console.log(data);
});
這樣不管你在哪個資料夾執行,都能正確找到 data/file.txt。
今天我們學到:
join vs resolve → 前者拼接路徑,後者算絕對路徑。
basename、extname、dirname → 快速取檔名、附檔名與目錄。
parse 與 format → 路徑物件與字串互轉。
⚠️ 陷阱:相對路徑依賴「執行位置」,不是檔案位置。
→ 解法:搭配 __dirname + path.resolve()。